
import com.facebook.thrift.TDeserializer;
import com.facebook.thrift.TSerializer;
import com.facebook.thrift.protocol.TCompactProtocol;
import dv.Epoch;
import dv.apkeep.Model;
import dv.network.Device;
import dv.network.Network;
import dv.network.Port;
import dv.network.Rule;
import org.zeromq.SocketType;
import org.zeromq.ZMQ;
import org.zeromq.ZContext;
import tft.*;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println("start");
        HashMap<Long, Epoch> epochMap = new HashMap<>();


        TDeserializer deserializer = new TDeserializer(new TCompactProtocol.Factory());

        try (ZContext context = new ZContext()) {
            // Socket to talk to clients
            ZMQ.Socket socket = context.createSocket(SocketType.SUB);
            socket.connect("tcp://192.168.2.1:60001");
            socket.subscribe("");
            while (true) {
                RouteDatabase routeDatabase = new RouteDatabase();
                Publication pub = new Publication();
                byte[] data = socket.recv();
                deserializer.deserialize(pub, data);
                for (Map.Entry<String, Value> entry : pub.keyVals.entrySet()) {
                    if (entry.getKey().contains("epoch")) {
                        System.out.println("KVUpdate " + System.nanoTime());
                        deserializer.deserialize(routeDatabase, entry.getValue().getValue());
                        long epochId = routeDatabase.epochId;
                        if (!epochMap.containsKey(epochId)) {
                            epochMap.put(epochId, new Epoch(epochId, createNetwork()));
                            System.out.println("new epoch " + System.nanoTime());
                        }
                        Epoch epoch = epochMap.get(epochId);
                        epoch.addFib(routeDatabase);
//                        network.addRule(network.getDevices().get(routeDatabase.thisNodeName), rule);
                    }
                }
            }
        }
    }

    public static Network createNetwork() {
        Network network = new Network();
        String[] devicenames = {"r1", "r2", "r3", "r4"};

        // add a default device for blackholes, since dfg need a peer node for default port of each device
        network.addDevice("default");
        for (String name : devicenames) {
            network.addDevice(name);
            network.addLink(name, "default", "default", name + "-peer-default");
        }
        /*
r1-eth0<->r2-eth0 (OK OK)
r2-eth1<->r3-eth0 (OK OK)
r3-eth1<->r4-eth0 (OK OK)
r4-eth1<->r1-eth1 (OK OK)
         */
        network.addLink("r1", "r1-eth0", "r2", "r2-eth0");
        network.addLink("r2", "r2-eth1", "r3", "r3-eth0");
        network.addLink("r3", "r3-eth1", "r4", "r4-eth0");
        network.addLink("r4", "r1-eth1", "r2", "r1-eth1");
        Model model = new Model(network);
        network.setModel(model);

        for (Device device : network.getDevices().values()) {
            Port defaultPort = device.addPortByName(device.getName() + "-default");
            Rule rule = new Rule(BigInteger.valueOf(0), 0, defaultPort);
            network.addRule(device, rule);
            model.portToPreds.put(defaultPort, new HashSet<>());
            model.portToPreds.get(defaultPort).add(1);
            model.predToPorts.get(1).add(defaultPort);
        }
        return network;
    }
}
